﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Security;
using System.Security;

namespace System
{
    /// <summary>
    /// StringExtensions Library
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// Shear String
        /// </summary>
        /// <param name="value">Original String</param>
        /// <param name="lenght">Shear Lenght</param>
        /// <param name="endstring">Ending String</param>
        /// <returns>String of Shear</returns>
        public static string Substring(this string value, int lenght, string endstring)
        {
            return value.Length > lenght ? value.Substring((int)uint.MinValue, lenght) + endstring : value;
        }

        /// <summary>
        /// Shear String
        /// </summary>
        /// <param name="value">Original String</param>
        /// <param name="lenght">Shear Lenght</param>
        /// <returns>String of Shear</returns>
        public static string Substring(this string value, int lenght)
        {
            return value.Substring(lenght, string.Empty);
        }

        /// <summary>
        /// Comparing strings
        /// </summary>
        /// <param name="value">Original String</param>
        /// <param name="pattern">pattern</param>
        /// <returns>is match</returns>
        public static Match Match(this string value, string pattern, RegexOptions regexoptions = RegexOptions.IgnoreCase)
        {
            return Regex.Match(value, pattern, regexoptions);
        }

        /// <summary>
        /// Comparing strings
        /// </summary>
        /// <param name="value">Original String</param>
        /// <param name="pattern">pattern</param>
        /// <param name="regexoptions">regexoptions</param>
        /// <returns>is match</returns>
        public static bool IsMatch(this string value, string pattern, RegexOptions regexoptions = RegexOptions.IgnoreCase)
        {
            return Regex.IsMatch(value, pattern, regexoptions);
        }

        /// <summary>
        /// Replace string
        /// </summary>
        /// <param name="value">orginal string</param>
        /// <param name="RegexString">Replace of string</param>
        /// <returns>return value</returns>
        public static string Replace(this string value, string replacement, string regexString, RegexOptions regexOptions = RegexOptions.IgnoreCase)
        {
            return Regex.Replace(value, regexString, replacement, regexOptions);
        }

        /// <summary>
        /// MakePassword for md5 or sha1
        /// </summary>
        /// <param name="value">orginal string</param>
        /// <param name="pf">format</param>
        /// <returns>return value</returns>
        public static string Password(this string value, PasswordFormat pf)
        {
            switch (pf)
            {
                case PasswordFormat.MD5:
                    return Encrypt.MD5(value);

                case PasswordFormat.SHA1:
                    return Encrypt.SHA1(value);
            }

            return value;
        }

        /// <summary>
        /// Check String is Null or Empty
        /// </summary>
        /// <param name="value">value</param>
        /// <returns>is null or empty</returns>
        public static bool IsNullOrEmpty(this string value)
        {
            return value == null || value == string.Empty;
        }


        #region StringConstantsToHelpWithComparisons

        private const string m_Letters = "abcdefghijklmnopqrstuvwxyz";
        private const string m_Digits = "0123456789";
        private const string m_ForwardSlash = "/";
        private const string m_BackSlash = "\\";
        private const string m_Period = ".";
        private const string m_DollarSign = "$";
        private const string m_PercentSign = "%";
        private const string m_Comma = ",";
        private const string m_Yes = "yes";
        private const string m_No = "no";
        private const string m_True = "true";
        private const string m_False = "false";
        private const string m_1 = "1";
        private const string m_0 = "0";
        private const string m_y = "y";
        private const string m_n = "n";

        #endregion

        #region DataTypeStringConstants

        public const string m_GUID = "guid";
        public const string m_Boolean1 = "boolean";
        public const string m_Boolean2 = "bool";
        public const string m_Byte = "byte";
        public const string m_Char = "char";
        public const string m_DateTime = "datetime";
        public const string m_DBNull = "dbnull";
        public const string m_Decimal = "decimal";
        public const string m_Double = "double";
        public const string m_Empty = "empty";
        public const string m_Int16_1 = "int16";
        public const string m_Int16_2 = "short";
        public const string m_Int32_1 = "int32";
        public const string m_Int32_2 = "int";
        public const string m_Int32_3 = "integer";
        public const string m_Int64_1 = "int64";
        public const string m_Int64_2 = "long";
        public const string m_Object = "object";
        public const string m_SByte = "sbyte";
        public const string m_Single = "single";
        public const string m_String = "string";
        public const string m_UInt16 = "uint16";
        public const string m_UInt32 = "uint32";
        public const string m_UInt64 = "uint64";

        #endregion

        /// <summary>
        /// Evaluates whether passed-in string can be converted to a bool
        /// </summary>
        /// <param name="stream">string to check</param>
        /// <returns>
        /// bool indicating whether stream is a bool (0, 1, true/True, 
        /// false/False)
        /// </returns>
        public static bool IsStandardBool(this string stream)
        {
            try
            {
                if (stream.IsNullOrEmpty()) return false;

                stream = stream.Trim().ToLower();

                switch (stream)
                {
                    case m_0:
                        return true;
                    case m_1:
                        return true;
                    case m_True:
                        return true;
                    case m_False:
                        return true;
                    default:
                        return false;
                }
            }
            catch (Exception)
            {
                //ErrorTool.ProcessError(ex);
                return false;
            }
        }

        /// <summary>
        /// Evaluates whether string can can be COERCED to a bool
        /// </summary>
        /// <param name="stream">string to check</param>
        /// <returns>
        /// bool indicating whether argument is a standard or custom bool 
        /// (0, 1, true/True, false/False) OR (y/Y, yes/Yes, n/N, no/NO) 
        /// </returns>
        public static bool IsFriendlyBool(this string stream)
        {
            try
            {
                if (stream.IsNullOrEmpty()) return false;

                stream = stream.Trim().ToLower();

                switch (stream)
                {
                    case m_0:
                        return true;
                    case m_1:
                        return true;
                    case m_True:
                        return true;
                    case m_False:
                        return true;
                    case m_n:
                        return true;
                    case m_y:
                        return true;
                    case m_No:
                        return true;
                    case m_Yes:
                        return true;
                    default:
                        return false;
                }
            }
            catch (Exception)
            {
                //ErrorTool.ProcessError(ex);
                return false;
            }
        }

        /// <summary>
        /// Checks each character of the string for any character other 
        /// than a digit, or a dollar sign or a percentage sign. If any
        /// are found, returns false indicating that the stream is NOT
        /// a number
        /// </summary>
        /// <param name="stream">
        /// The stream of characters (string) to check
        /// </param>
        /// <returns>
        /// True/False value indicating whether the string can be 
        /// coerced to a number
        /// </returns>
        public static bool IsNumber(this string stream)
        {
            if (stream.IsNullOrEmpty() || !Regex.IsMatch(stream, string.Format("^[0123456789.]+$", m_Digits, m_Period)))
                return false;
            else
                return true;
        }

        /// <summary>
        /// Checks whether string can be coerced into a DateTime value
        /// </summary>
        /// <param name="stream">The string to check/// </param>
        /// <returns>
        /// bool indicating whether stream can be converted to a date
        /// </returns>
        public static bool IsDate(this string stream)
        {
            try
            {
                DateTime.Parse(stream);

                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Checks the string to see whether it is a number and if it is, 
        /// then it checks whether there is a decimal in that number.
        /// </summary>
        /// <param name="stream">
        /// The stream of characters (string) to check
        /// </param>
        /// <returns>
        /// True/False value indicating whether the string is a
        /// double---must be a number, and include a decimal 
        /// in order to pass the test
        /// </returns>
        public static bool IsDouble(this string stream)
        {
            if (!IsNumber(stream))
                return false;

            //at this point each character is a number OR an allowable
            //symbol; we must see whether the string holds the decimal:
            if (stream.Contains(m_Period))
                return true;

            //still here? the stream is a #, but does NOT have a decimal
            return false;
        }

        /// <summary>
        /// Checks string to see if it matches a TypeCode string and returns
        /// that TypeCode, or returns TypeCode.Empty if there is no match
        /// </summary>
        /// <param name="dataTypeString">
        /// String representation of a TypeCode (string, int, bool...)
        /// </param>
        /// <returns>TypeCode that maps to the dataTypeString</returns>
        public static TypeCode GetDataType(this string dataTypeString)
        {
            switch (dataTypeString.ToLower())
            {
                // todo: isn't there a better way for guid?
                case m_GUID:
                    return TypeCode.Object;
                case m_Boolean1:
                    return TypeCode.Boolean;
                case m_Boolean2:
                    return TypeCode.Boolean;
                case m_Byte:
                    return TypeCode.Byte;
                case m_Char:
                    return TypeCode.Char;
                case m_DateTime:
                    return TypeCode.DateTime;
                case m_DBNull:
                    return TypeCode.DBNull;
                case m_Decimal:
                    return TypeCode.Decimal;
                case m_Double:
                    return TypeCode.Double;
                case m_Empty:
                    return TypeCode.Empty;
                case m_Int16_1:
                    return TypeCode.Int16;
                case m_Int16_2:
                    return TypeCode.Int16;
                case m_Int32_1:
                    return TypeCode.Int32;
                case m_Int32_2:
                    return TypeCode.Int32;
                case m_Int32_3:
                    return TypeCode.Int32;
                case m_Int64_1:
                    return TypeCode.Int64;
                case m_Int64_2:
                    return TypeCode.Int64;
                case m_Object:
                    return TypeCode.Object;
                case m_SByte:
                    return TypeCode.SByte;
                case m_Single:
                    return TypeCode.Single;
                case m_String:
                    return TypeCode.String;
                case m_UInt16:
                    return TypeCode.UInt16;
                case m_UInt32:
                    return TypeCode.UInt32;
                case m_UInt64:
                    return TypeCode.UInt64;
                default:
                    return TypeCode.Empty;
            }
        }

        /// <summary>
        /// Returns a date, as coerced from the string argument. Will raise
        /// an error, if the string cannot be coerced 
        /// ----so, use IsDate in this same class FIRST
        /// </summary>
        /// <param name="stream">string to get date value from</param>
        /// <returns>a DateTime object</returns>
        public static DateTime GetDate(this string stream)
        {
            DateTime dateValue = new DateTime();
            try
            {
                dateValue = DateTime.Parse(stream);

                return dateValue;
            }
            catch (Exception)
            {
                //ErrorTool.ProcessError(ex);
                return dateValue;
            }
        }

        /// <summary>
        /// Returns an int, as coerced from the string argument. 
        /// Will raise an error, if the string cannot be coerced
        /// ----so, use IsNumber in this same class FIRST
        /// </summary>
        /// <param name="stream">string to get int value from</param>
        /// <returns>an int object</returns>
        public static int GetInteger(this string stream)
        {
            if (!IsNumber(stream)) return 0;

            //we've removed superfluous formatting characters, if they 
            //did exist, now let's round it/convert it, and return it:
            return Convert.ToInt32(stream.Match("[^\\.]+").Value);
        }

        /// <summary>
        /// Returns a double, as coerced from the string argument. 
        /// Will raise an error, if the string cannot be coerced
        /// ----so, use IsNumber in this same class FIRST
        /// </summary>
        /// <param name="stream">string to get double value from</param>
        /// <returns>a double object</returns>
        public static double GetDouble(this string stream)
        {
            if (!IsNumber(stream)) return 0;

            //we've removed superfluous formatting characters, if they 
            //did exist, now let's round it/convert it, and return it:
            return Convert.ToDouble(stream);
        }

        /// <summary>
        /// Iterates thru a string, and removes anything set to clean.
        /// Except---Does NOT remove anything in exceptionsToAllow
        /// </summary>
        /// <param name="stream">
        /// The string to clean</param>
        /// <returns>
        /// The same string, missing all elements that were set to clean
        /// (except when a character was listed in exceptionsToAllow)
        /// </returns>
        public static string Clean(this string stream, bool cleanWhiteSpace = false, bool cleanDigits = false)
        {
            var temp = stream;

            if (cleanWhiteSpace)
                temp = Replace(stream, string.Empty, " ");

            if (cleanDigits)
                temp = Replace(stream, string.Empty, "[1234567890]");

            return temp;

        }
    }
}
